package com.oauth.config;

import com.oauth.TickGranter.TickGranter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.codec.ByteArrayEncoder;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.stereotype.Service;


import javax.annotation.Resource;
import javax.sql.DataSource;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;


/**
 * @author ysy
 */
@Slf4j
@Configuration
public class MyOauthServiceConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    /**#######################认证需要的对象#######################*/
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    public UserDetailsService userDetailsService;
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource){
        // return new InMemoryAuthorizationCodeServices(); //内存模式
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Qualifier("accessTokenConverter")
    @Autowired
    public JwtAccessTokenConverter accessTokenConverter;

    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        //客户端详情服务
        service.setClientDetailsService(clientDetailsService);
        //允许令牌自动刷新
        service.setSupportRefreshToken(true);
        //令牌存储策略-内存
        service.setTokenStore(tokenStore);
        // 用jwt
        service.setTokenEnhancer(accessTokenConverter);
        // 令牌默认有效期2小时
        service.setAccessTokenValiditySeconds(7200);
        // 刷新令牌默认有效期3天
        service.setRefreshTokenValiditySeconds(259200);
        return service;
    }
    /**#######################认证需要的对象END#######################*/


    /**#######################认证中的token需要需要的对象#######################*/
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private TokenStore tokenStore;
//    @Bean
//    public TokenStore tokenStore(){
//    //使用基于内存的普通令牌
//        return new InMemoryTokenStore();
//    }

    /**#######################认证中的token需要需要的对象#######################*/






    //设置授权码模式的授权码如何存取，暂时用内存方式。
//    @Bean
//    public AuthorizationCodeServices authorizationCodeServices(){
//        return new InMemoryAuthorizationCodeServices();
//    }



    /**
     * 用来配置令牌端点的安全约束.
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // oauth/token_key公开
        security.tokenKeyAccess("permitAll()")
                // oauth/check_token公开.所匹配的 URL ,都需要被认证才能访问
                //
                .checkTokenAccess("permitAll()")
                // 表单认证，申请令牌
                .allowFormAuthenticationForClients();
    }

    /**
     * 客户端的认证配置,用来配置客户端详情服务
     * （ClientDetailsService），客户端详情信息在这里进行初始化，你能够把客户端详
     * 情信息写死在这里或者是通过数据库来存储调取详情信息。
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       // 数据库模式，需要创建表oauth_client_details
        clients.jdbc(dataSource);

        //内存配置的方式配置用户信息
//        clients.inMemory()//内存方式
//                .withClient("c1") //client_id
//                .secret(new BCryptPasswordEncoder().encode("secret"))//客户端秘钥
//                .resourceIds("order")//客户端拥有的资源列表
//                .authorizedGrantTypes("authorization_code",
//                        "password", "client_credentials", "implicit",
//                        "refresh_token")//该client允许的授权类型
//                .scopes("all")//允许的授权范围
//                .autoApprove(true)//跳转到授权页面
//                .redirectUris("http://www.baidu.com");//回调地址
// .and() //继续注册其他客户端
// .withClient()
// ...
// 加载自定义的客户端管理服务
// clients.withClientDetails(clientDetailsService);
    }


    /**
     * 进行认证的模块
     * 用来配置令牌（token）的访问
     * 端点和令牌服务(tokenservices)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      //  super.configure(endpoints);
        endpoints
                // 定制授权同意页面
               // .pathMapping("/oauth/confirm_access","/customer/confirm_access")
                //认证管理器
              //  .authenticationManager(authenticationManager)
                //密码模式的用户信息管理
              //  .userDetailsService(userDetailsService)
                // 授权码服务
              //  .authorizationCodeServices(authorizationCodeServices)
                // 自定义的认证方式
                .tokenGranter(new CompositeTokenGranter(initGranters(endpoints)))
                //令牌管理服务
                .tokenServices(tokenService())

                // 请求认证的方式
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);


        //加入自定义token-----------------------------------------
//        endpoints
//                .authenticationManager(authenticationManager)
//                .userDetailsService(userDetailsService)
//                // 自定义的认证方式
//                .tokenGranter(new CompositeTokenGranter(initGranters(endpoints)))
//                //令牌管理服务
//                .tokenStore(tokenStore())
//                .accessTokenConverter(accessTokenConverter())
//                .tokenEnhancer(tokenEnhancerChain)
//                .exceptionTranslator(e -> {
//                    if (e instanceof InvalidGrantException){
//                        return ResponseEntity
//                                .status(HttpStatus.UNAUTHORIZED)
//                                .body(new UserOauthException("用户名或密码错误"));
//                    }else if (e instanceof OAuth2Exception) {
//                        OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
//                        return ResponseEntity
//                                .status(oAuth2Exception.getHttpErrorCode())
//                                .body(new UserOauthException(oAuth2Exception.getMessage()));
//
//                    } else if (e instanceof AuthenticationException) {
//                        e.printStackTrace();
//                        if (e.getCause().getCause() instanceof CustomOauthException) {
//                            return ResponseEntity
//                                    .status(HttpStatus.UNAUTHORIZED)
//                                    .body(new UserOauthException(e.getCause().getCause().getMessage()));
//                        }
//                        return ResponseEntity
//                                .status(HttpStatus.UNAUTHORIZED)
//                                .body(new UserOauthException(e.getMessage()));
//                    } else {
//                        log.error("Exception", e);
//                        throw e;
//                    }
//                });

    }

    public List<TokenGranter> initGranters(AuthorizationServerEndpointsConfigurer endpoints) {

        List<TokenGranter> tokenGranterList = new ArrayList<>();

        AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
        ClientDetailsService clientDetailsService = endpoints.getClientDetailsService();
        OAuth2RequestFactory oAuth2RequestFactory = endpoints.getOAuth2RequestFactory();
        // 1.自定义的认证模式
        tokenGranterList.add(new TickGranter(authenticationManager,tokenServices,clientDetailsService,oAuth2RequestFactory));
        // 2.用户名称和密码
        tokenGranterList.add(new ResourceOwnerPasswordTokenGranter(authenticationManager,tokenServices,clientDetailsService,oAuth2RequestFactory));
        // 3.简模式
         tokenGranterList.add(new ImplicitTokenGranter(tokenServices,clientDetailsService,oAuth2RequestFactory));
        // 4.刷新token模式
         tokenGranterList.add(new RefreshTokenGranter(tokenServices,clientDetailsService,oAuth2RequestFactory));
        // 5.授权码
         AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
         tokenGranterList.add(new AuthorizationCodeTokenGranter(tokenServices,authorizationCodeServices,clientDetailsService,oAuth2RequestFactory));
        // 6.客户端模式
        tokenGranterList.add(new ClientCredentialsTokenGranter(tokenServices,clientDetailsService,oAuth2RequestFactory));
         return tokenGranterList;
    }


}
